package com.yanyeori.framework.websocket.server;

import com.yanyeori.framework.core.util.JacksonUtil;
import com.yanyeori.framework.websocket.message.WsMessage;
import com.yanyeori.framework.websocket.message.WsMessageBack;
import com.yanyeori.framework.websocket.message.WsTopicEnum;
import lombok.extern.slf4j.Slf4j;

import javax.websocket.*;

/**
 * WebSocket Server
 *
 * @author chenkuan
 * @since 2021/6/3
 */
@Slf4j
public abstract class AbstractWebSocketServer {

    @OnOpen
    public void onOpen(Session session) {
        log.info("SessionId [{}] connected", session.getId());
        WebSocketUtil.addSession(session);
        onOpenBack(session);
    }

    @OnClose
    public void onClose(Session session) {
        log.info("SessionId [{}] disconnect", session.getId());
        WebSocketUtil.removeSession(session);
        onCloseBack(session);
    }

    @OnMessage
    public void onMessage(Session session, String message) {
        log.debug("Received message: {}", message);
        WsMessage wsMessage = JacksonUtil.parseObject(message, WsMessage.class);
        WsTopicEnum wsTopicEnum = WsTopicEnum.getByType(wsMessage.getTopic());
        if (wsTopicEnum == null) {
            onMessageBack(new WsMessageBack(wsMessage.getTopic(), JacksonUtil.toJson(wsMessage.getData())));
            return;
        }
        switch (wsTopicEnum) {
            case HEARTBEAT:
                sendBySession(session, wsTopicEnum, "Reply heartbeat message");
                log.debug("Reply heartbeat message to sessionId [{}]", session.getId());
                break;
            case SUBSCRIBE:
                String subscribeMsg = WebSocketUtil.subscribe(session.getId(), wsMessage.getData().toString());
                sendBySession(session, wsTopicEnum, subscribeMsg);
                log.debug("SessionId [{}] subscribe topic [{}]", session.getId(), wsMessage.getData().toString());
                break;
            case UNSUBSCRIBE:
                String unsubscribeMsg = WebSocketUtil.unsubscribe(session.getId(), wsMessage.getData().toString());
                sendBySession(session, wsTopicEnum, unsubscribeMsg);
                log.debug("SessionId [{}] unsubscribe topic [{}]", session.getId(), wsMessage.getData().toString());
                break;
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket error", error);
        onErrorBack(session);
    }

    protected abstract void onOpenBack(Session session);

    protected abstract void onCloseBack(Session session);

    protected abstract void onMessageBack(WsMessageBack message);

    protected abstract void onErrorBack(Session session);

    private void sendBySession(Session session, WsTopicEnum wsTopicEnum, Object message) {
        try {
            session.getBasicRemote().sendText(JacksonUtil.toJson(new WsMessage(wsTopicEnum.getType(), message)));
        } catch (Exception e) {
            throw new RuntimeException(wsTopicEnum.getMsg() + " message sending exception", e);
        }
    }
}
